home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / pty4 / part04 < prev    next >
Encoding:
Text File  |  1992-02-18  |  57.0 KB  |  1,960 lines

  1. Newsgroups: comp.sources.unix
  2. From: brnstnd@nyu.edu (Dan Bernstein)
  3. Subject: v25i130: Generalized interface to pseudo-tty devices, Part04/09
  4. Sender: unix-sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: brnstnd@nyu.edu (Dan Bernstein)
  8. Posting-Number: Volume 25, Issue 130
  9. Archive-Name: pty4/part04
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 4 (of 9)."
  18. # Contents:  BLURB INSTALL.c NOTES SECURITY SYSCONF TESTS checkptys.c
  19. #   env.c fmt.c scan.c
  20. # Wrapped by vixie@cognition.pa.dec.com on Wed Feb 19 13:35:04 1992
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'BLURB' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'BLURB'\"
  24. else
  25. echo shar: Extracting \"'BLURB'\" \(4340 characters\)
  26. sed "s/^X//" >'BLURB' <<'END_OF_FILE'
  27. pty is meant as the sole interface between pseudo-terminals and the rest
  28. of the system. Rich Salz said of pty 3.0: ``This is the Ginsu knife (it
  29. slices, it dices, it never rusts) that Dan has been talking about in
  30. comp.unix.wizards/internals for some time now. It is a mind-blower.''
  31. But I just couldn't leave well enough alone, so here's pty 4.0, a vastly
  32. improved rewrite of the entire package. A taste of what it has to offer:
  33. X
  34. X* Improved security - pty 3.0 offered tty security ahead of its time---
  35. several months afterwards, Sun released a ``critical'' security patch
  36. with essentially the same security tests. Now pty 4.0 offers proven
  37. security. Although you can install and use the package without
  38. privileges, system administrators can install pty 4.0 so that it
  39. X_guarantees_ that nobody else has access to your tty. I'm offering a
  40. cash reward for anyone who can subvert these guarantees.
  41. X
  42. X* Session management - If you run your shell under pty, and the
  43. connection is hung up, you can log in again and reconnect. The session
  44. management model is extremely simple---it has just three primitives---
  45. yet powerful enough to accomplish tricky tasks, such as recording the
  46. output from a process after the process has started. A paper in the
  47. package, ``An introduction to session management,'' leads even novice
  48. users through competent use of session management commands.
  49. X
  50. X* Automatic installation - pty 4.0 comes with a completely automated
  51. configure/compile/install/verify-configuration setup. It will configure
  52. itself properly for most popular systems without human input.
  53. X
  54. X* Modularity - When I say that pty is meant as the sole interface to
  55. pseudo-terminals, I mean it! pty doesn't get in the way of direct,
  56. efficient pseudo-terminal I/O. So you can use it as a component of other
  57. programs which add input line editing, virtual screen support, or other
  58. fancy features. pty handles just one job, and handles it so cleanly that
  59. you'll never have to duplicate pseudo-terminal code in another program.
  60. X
  61. X* Free utilities - pty 4.0 comes with even more useful utilities than
  62. pty 3.0. It includes ten improved clones of standard utilities, notably
  63. a version of ``script'' which makes a proper utmp entry; and thirty new
  64. tools ranging from administrative helpers to ``tscript'', which records
  65. an interactive session _including the timing between characters_. Power
  66. users will appreciate ``nobuf'', which uses pty to transparently turn
  67. off stdio buffering in any program.
  68. X
  69. X* Free libraries - The pty package comes with several of my favorite
  70. libraries: env, fmt, getopt, radixsort, ralloc, scan, sigdfl, sigsched,
  71. sod, timer, username. You can use all of these for your own programs.
  72. X
  73. X* POSIX support - pty 4.0 works without trouble under popular POSIX/BSD
  74. systems, including Ultrix 4.1 and SunOS 4.1.1. All the job control
  75. features have been adapted to work with POSIX job control. pty should
  76. also be included with BSD 4.4.
  77. X
  78. X* Detailed documentation - Let your worries about incomplete program
  79. documentation be over. The pty package includes more than five thousand
  80. lines of documentation: forty quick-reference man pages; papers on
  81. controlling ttys, job control, session management, and user log files;
  82. extensive notes on pty internals and porting issues; and more.
  83. X
  84. Okay, enough hype. What's pty good for? Once upon a time nethack would,
  85. if you were lucky, produce characters with both a ring of polymorph and
  86. a ring of polymorph control. I wanted to run nethack inside a script
  87. which would keep rerolling characters until it saw that combination.
  88. X(Playing by the rules was never my forte.) Unfortunately, nethack didn't
  89. like having its input and output redirected. So I wrote the first
  90. versions of pty. ``pty nethack'' worked just like ``nethack'' but could
  91. be invoked in the middle of a pipe inside a script. As the years went
  92. by, pty became somewhat more powerful and flexible, but its basic
  93. function has always remained the same: to run programs, especially
  94. X``interactive'' programs, under a pseudo-tty. Despite this single-minded
  95. attitude, pty has wormed its way into the solutions to dozens of
  96. problems, ranging from buffer control to automating telnet scripts to
  97. making rlogind secure. Pseudo-terminal code seems to spring up
  98. everywhere; pty is your weapon to slash that code to a single line.
  99. XEnjoy!
  100. X
  101. X---Dan Bernstein, brnstnd@nyu.edu
  102. END_OF_FILE
  103. if test 4340 -ne `wc -c <'BLURB'`; then
  104.     echo shar: \"'BLURB'\" unpacked with wrong size!
  105. fi
  106. # end of 'BLURB'
  107. fi
  108. if test -f 'INSTALL.c' -a "${1}" != "-c" ; then 
  109.   echo shar: Will not clobber existing file \"'INSTALL.c'\"
  110. else
  111. echo shar: Extracting \"'INSTALL.c'\" \(6231 characters\)
  112. sed "s/^X//" >'INSTALL.c' <<'END_OF_FILE'
  113. X#include <pwd.h>
  114. X#include <grp.h>
  115. X#include <stdio.h>
  116. X#include <sys/types.h>
  117. X#include <sys/file.h>
  118. X#include <sys/stat.h>
  119. X#include "config/ptybin.h"
  120. X#include "config/ptydir.h"
  121. X#include "config/ptygroup.h"
  122. X#include "config/sessconnfile.h"
  123. X#include "config/sessfile.h"
  124. X#include <utmp.h>
  125. X#include "config/utmpfile.h"
  126. X#include "config/wtmpfile.h"
  127. X#include "config/ptygroup.h"
  128. X
  129. char ptybin[] = PTYBIN;
  130. char ptydir[] = PTYDIR;
  131. char sessconnnow[] = SESSCONNNOW_FILE;
  132. char sessconnlog[] = SESSCONNLOG_FILE;
  133. char sessnow[] = SESSNOW_FILE;
  134. char sesslog[] = SESSLOG_FILE;
  135. char utmp[] = UTMP_FILE;
  136. char wtmp[] = WTMP_FILE;
  137. X
  138. static int num = 0;
  139. X
  140. void section(s)
  141. char *s;
  142. X{
  143. X ++num;
  144. X printf("\n%d. %s.\n",num,s);
  145. X}
  146. X
  147. int dontskip(s,t,u)
  148. char *s;
  149. char *t;
  150. char *u;
  151. X{
  152. X char buf[100];
  153. X char format[200];
  154. X sprintf(format,"! %s: ",s);
  155. X printf(format,t,u);
  156. X if (fgets(buf,sizeof(buf),stdin) == 0)
  157. X   return 0;
  158. X if (buf[0] == 'o')
  159. X  {
  160. X   puts("Okay.");
  161. X   return 1;
  162. X  }
  163. X if (buf[0] == 's')
  164. X  {
  165. X   puts("Skipped.");
  166. X   return 0;
  167. X  }
  168. X return 1;
  169. X}
  170. X
  171. copyf2d(fn,dirfn)
  172. char *fn;
  173. char *dirfn;
  174. X{
  175. X int fdold;
  176. X int fdnew;
  177. X int r;
  178. X int n;
  179. X int w;
  180. X char buf[16384];
  181. X
  182. X fdold = open(fn,O_RDONLY);
  183. X if (fdold == -1)
  184. X   return -1;
  185. X fdnew = open(dirfn,O_WRONLY | O_CREAT | O_TRUNC,0600);
  186. X if (fdnew == -1)
  187. X  { close(fdold); return -1; }
  188. X while ((r = read(fdold,buf,sizeof(buf))) > 0)
  189. X  {
  190. X   n = 0;
  191. X   while (n < r)
  192. X    {
  193. X     w = write(fdnew,buf + n,r - n);
  194. X     if (w == -1)
  195. X      {
  196. X       close(fdold); close(fdnew); return -1;
  197. X      }
  198. X     n += w;
  199. X    }
  200. X  }
  201. X close(fdnew);
  202. X close(fdold);
  203. X if (r == -1)
  204. X   return -1;
  205. X return 0;
  206. X}
  207. X
  208. static char *ptyuname = "pty";
  209. X
  210. chownpty(fn)
  211. char *fn;
  212. X{
  213. X struct passwd *own;
  214. X own = getpwnam(ptyuname);
  215. X if (!own)
  216. X   return -1;
  217. X return chown(fn,own->pw_uid,-1);
  218. X}
  219. X
  220. chgrptty(fn)
  221. char *fn;
  222. X{
  223. X struct group *grp;
  224. X grp = getgrnam("tty");
  225. X if (!grp)
  226. X   return -1;
  227. X return chown(fn,-1,grp->gr_gid);
  228. X}
  229. X
  230. touch(fn)
  231. char *fn;
  232. X{
  233. X int fd;
  234. X fd = open(fn,O_WRONLY | O_CREAT,0644);
  235. X if (fd == -1)
  236. X   return -1;
  237. X close(fd);
  238. X if (chmod(fn,0644) == -1)
  239. X   return -1;
  240. X if (chownpty(fn) == -1)
  241. X   return -1;
  242. X return 0;
  243. X}
  244. X
  245. static char CHOPTYSS[100] = "chown pty %s/%s";
  246. X
  247. dobin(fn,level)
  248. char *fn;
  249. int level;
  250. X{
  251. X char dirfn[sizeof(ptybin) + 50];
  252. X sprintf(dirfn,"%s/%s",ptybin,fn);
  253. X if (dontskip("cp %s %s",fn,ptybin))
  254. X   if (copyf2d(fn,dirfn) == -1)
  255. X     perror("copy failed");
  256. X switch(level)
  257. X  {
  258. X   case 0:
  259. X     if (dontskip("chmod 755 %s/%s",ptybin,fn))
  260. X       if (chmod(dirfn,0755) == -1)
  261. X     perror("chmod: cannot change mode");
  262. X     break;
  263. X   case 1:
  264. X     if (dontskip("chmod 755 %s/%s",ptybin,fn))
  265. X       if (chmod(dirfn,0755) == -1)
  266. X     perror("chmod: cannot change mode");
  267. X     break;
  268. X   case 2:
  269. X     if (dontskip("chgrp tty %s/%s",ptybin,fn))
  270. X       if (chgrptty(dirfn) == -1)
  271. X     perror("chgrp: cannot change group");
  272. X     if (dontskip("chmod 2755 %s/%s",ptybin,fn))
  273. X       if (chmod(dirfn,02755) == -1)
  274. X     perror("chmod: cannot change mode");
  275. X     break;
  276. X   case 3:
  277. X     if (dontskip(CHOPTYSS,ptybin,fn))
  278. X       if (chownpty(dirfn) == -1)
  279. X     perror("chown: cannot change owner");
  280. X     if (dontskip("chmod 4755 %s/%s",ptybin,fn))
  281. X       if (chmod(dirfn,04755) == -1)
  282. X     perror("chmod: cannot change mode");
  283. X     break;
  284. X  }
  285. X}
  286. X
  287. main(argc,argv)
  288. int argc;
  289. char *argv[];
  290. X{
  291. X if (argv[1])
  292. X  {
  293. X   ptyuname = argv[1];
  294. X   sprintf(CHOPTYSS,"chown %s %%s/%%s",ptyuname);
  295. X  }
  296. X printf("I assume you've already set up a %s user and a tty (%d) group.\n\n",ptyuname,PTYGROUP);
  297. X printf("Each action will be printed before it is run. Press return to proceed.\n");
  298. X printf("Type skip (or anything beginning with an s) to skip a step.\n");
  299. X
  300. X section("Make pty session directory");
  301. X if (dontskip("mkdir %s",ptydir,""))
  302. X   if (mkdir(ptydir,0700) == -1)
  303. X     perror("mkdir: cannot create directory");
  304. X if (dontskip("chown %s %s",ptyuname,ptydir))
  305. X   if (chownpty(ptydir) == -1)
  306. X     perror("chown: cannot change owner");
  307. X
  308. X section("Make pty binary directory");
  309. X if (dontskip("mkdir %s",ptybin,""))
  310. X   if (mkdir(ptybin,0700) == -1)
  311. X     perror("mkdir: cannot create directory");
  312. X if (dontskip("chmod 755 %s",ptybin,""))
  313. X   if (chmod(ptybin,0755) == -1)
  314. X     perror("chmod: cannot change mode");
  315. X
  316. X section("Make session and session-connection log files");
  317. X if (dontskip("touch %s",sessnow,""))
  318. X   if (touch(sessnow) == -1)
  319. X     perror("touch: cannot touch file");
  320. X if (dontskip("touch %s",sesslog,""))
  321. X   if (touch(sesslog) == -1)
  322. X     perror("touch: cannot touch file");
  323. X if (dontskip("touch %s",sessconnnow,""))
  324. X   if (touch(sessconnnow) == -1)
  325. X     perror("touch: cannot touch file");
  326. X if (dontskip("touch %s",sessconnlog,""))
  327. X   if (touch(sessconnlog) == -1)
  328. X     perror("touch: cannot touch file");
  329. X section("Make utmp and wtmp files (note: utmp will be owned by pty)");
  330. X if (dontskip("touch %s",utmp,""))
  331. X   if (touch(utmp) == -1)
  332. X     perror("touch: cannot touch file");
  333. X if (dontskip("touch %s",wtmp,""))
  334. X   if (touch(wtmp) == -1)
  335. X     perror("touch: cannot touch file");
  336. X
  337. X section("Copy executables into pty binary directory");
  338. X dobin("argv0",1);
  339. X dobin("biff",1);
  340. X dobin("checkptys",1);
  341. X dobin("condom",0);
  342. X dobin("ctrlv",1);
  343. X dobin("disconnect",3);
  344. X dobin("excloff",1);
  345. X dobin("exclon",1);
  346. X dobin("lock",1);
  347. X dobin("mesg",1);
  348. X dobin("nobuf",0);
  349. X dobin("pty",3);
  350. X dobin("reconnect",3);
  351. X dobin("script",0);
  352. X dobin("script.tidy",0);
  353. X dobin("sess",0);
  354. X dobin("sesskill",3);
  355. X dobin("sesslist",3);
  356. X dobin("sessmenu",1);
  357. X dobin("sessname",3);
  358. X dobin("sesswhere",1);
  359. X dobin("sesswho",1);
  360. X dobin("tiocsti",1);
  361. X dobin("tplay",1);
  362. X dobin("trecord",1);
  363. X dobin("tscript",0);
  364. X dobin("tty",1);
  365. X dobin("ttydetach",1);
  366. X dobin("ttyprotect",0);
  367. X dobin("users",1);
  368. X dobin("utmpinit",1);
  369. X dobin("waitfor",1);
  370. X dobin("wall",2);
  371. X dobin("who",1);
  372. X dobin("whoami",1);
  373. X dobin("write",2);
  374. X dobin("wtmprotate",0);
  375. X dobin("sessrotate",0);
  376. X dobin("sclogrotate",0);
  377. X dobin("sessnowinit",0);
  378. X dobin("scnowinit",0);
  379. X
  380. X section("Add log file rotations to daily, weekly, or monthly cron scripts");
  381. X printf("I'll leave this to you.\n");
  382. X printf("You may want to invoke wtmprotate, sessrotate, or sclogrotate.\n");
  383. X
  384. X section("Add utmp/sessnow/scnow initializations to /etc/rc.local");
  385. X printf("I'll leave this to you.\n");
  386. X printf("You may want to invoke utmpinit, sessnowinit, or scnowinit.\n");
  387. X
  388. X exit(0);
  389. X}
  390. END_OF_FILE
  391. if test 6231 -ne `wc -c <'INSTALL.c'`; then
  392.     echo shar: \"'INSTALL.c'\" unpacked with wrong size!
  393. fi
  394. # end of 'INSTALL.c'
  395. fi
  396. if test -f 'NOTES' -a "${1}" != "-c" ; then 
  397.   echo shar: Will not clobber existing file \"'NOTES'\"
  398. else
  399. echo shar: Extracting \"'NOTES'\" \(5508 characters\)
  400. sed "s/^X//" >'NOTES' <<'END_OF_FILE'
  401. Random implementation notes:
  402. X
  403. Master now runs with uid set to euid. This ensures the integrity of
  404. various log files at the expense of proper CPU time accounting. On the
  405. other hand, telnetd and rlogind run as root, so who cares?
  406. X
  407. The rotate scripts kludge a bit to preserve the owner of each log file.
  408. Any better solutions?
  409. X
  410. Makefile uses cc -E and makes several assumptions about cpp for creating
  411. wtmprotate and friends.
  412. X
  413. Certain C programs had control charaters, but they're gone. script.tidy
  414. has control characters, but script.tidy.sh doesn't---Makefile fixes it.
  415. X
  416. Note that the assumption of two-byte tty extensions runs throughout the
  417. code.
  418. X
  419. It might be good to have the master refuse info requests while preco.
  420. X
  421. If the signaller can't change to PTYDIR, or if it can't read the
  422. extension over a pipe from the master, or if various other
  423. pseudo-possible conditions happen, the master may end up disconnected
  424. even if it's not a session. The child will not run until the first
  425. successful reconnect.
  426. X
  427. Note that the master switches controlling ttys only as a service to the
  428. slave. Nothing in the pty code depends on having the new tty as a ctty.
  429. This is a good thing, as BSD does not provide any reliable way to
  430. dissociate from the current controlling terminal. (Exercise: Show how
  431. TIOCEXCL may be used to prevent dissociation.)
  432. X
  433. Note that -R forces -T---a lot of things could go wrong otherwise.
  434. X
  435. Note that the slave unblocks various signals and sets various others to
  436. SIG_DFL; this will undo any blocking set up before pty was invoked.
  437. X
  438. Under -R, the master will ignore the tostop setting on the original tty.
  439. There's no good way to solve this---if you consider it a problem.
  440. X
  441. There'd be a race between fdsigler and fdsig2us if both master and
  442. sigler wrote and blocked at once. The solution is simple: fdsig2us is
  443. X*ONLY* for sigler-to-master communication. The signaller never waits for
  444. a reply on an fdsig2us message; instead it listens attentively for other
  445. commands. (Similarly, fdsigler is only for master-to-sigler
  446. communication, but only one way is necessary to avoid deadlock.)
  447. X
  448. Sun broke the behavior of getdtablesize(); I don't care.
  449. X
  450. X
  451. X
  452. command to change flagreading?
  453. X
  454. at last possible moment, check all ifdefs
  455. X
  456. X
  457. should ttydetach trash all tty descriptors?
  458. X
  459. should write isatty() and friends, use a cache of /dev stats
  460. also, isatty() should look at tty* before others. devname.c...
  461. verify cached device->name mapping by doing one stat()?
  462. X
  463. if system runs out of fds, won't be able to talk to master. is this a
  464. problem?
  465. X
  466. if a program like sesslist gets stopped or blocked, master won't be able
  467. to reconnect. is this a problem?
  468. X
  469. ptysuspend -sp7 - suspend as if child had stopped
  470. ptycont -sp7  ???
  471. pty -xtp7  ?????
  472. X
  473. should checkptys do a pff /dev/tty*?
  474. X
  475. last - clone
  476. tachywho
  477. X
  478. if child stops while master is disconnected, stop will be held for
  479. reconnecting signaller; child will be automatically continued on
  480. reconnect
  481. X
  482. should probably ignore SIGPIPE for utilities
  483. X
  484. note that an independent sesslist may temporarily show master as
  485. disconnected
  486. X
  487. definitely should not optimize sigsched
  488. X
  489. X
  490. X
  491. from ptypaper.9:
  492. X: Instead of copying pseudo-terminal modes from the original terminal, pty
  493. X: will set up a generic new-discipline line-at-a-time mode. (Footnote:
  494. X: This generic mode is defined in tty_initmodes() in the pty source. It
  495. X: should instead be described in further detail in some manual page.)
  496. now defined in ttydfl.7.
  497. X: Note that pty handles job control
  498. X: correctly
  499. it now handles it even better than correctly.
  500. X: Note that a
  501. X: session ``program'' is always restarted when it is reconnected.
  502. still true
  503. X: TERM signals
  504. no longer---master-sigler communication uses file descriptors
  505. X: Note that if it weren't for WINCH handling and the kernel support for
  506. X: controlling terminals, all mention of the original tty could disappear
  507. X: from the master. This would greatly simplify reconnect, though some
  508. X: stopping complexity would move into the signaller.
  509. this is now done---winch and cttys are both handled differently now
  510. X: Like all programs, this pty implementation has its caveats. Because the
  511. X: master needs a tty to reconnect to, the reconnecting signaller has to
  512. X: have a file descriptor open and pointing to its current tty.
  513. no longer true
  514. X: As documented, pseudo-ttys cannot handle EOF.
  515. doesn't really matter now given nobuf
  516. X: Raw-mode tty processing is problematic. There is no way to set a
  517. X: terminal's modes so that it does no processing and acts just like a
  518. X: pipe.
  519. ditto
  520. X: pty's random pseudo-tty searching should not be in a fixed order.
  521. it isn't any more
  522. X: None of the above (except TIOCEXCL, but there are still races) address
  523. X: the problem of a background process keeping access to a pseudo-tty.
  524. X: pty's test for previous access is completely reliable under some
  525. X: variants of the BSD kernel, and completely unreliable under others.
  526. new test is (i think) completely reliable under all variants
  527. X: A much better solution is to have utmp initialized at system startup to
  528. X: list all available ptys, in the order that login requires; then pty will
  529. X: conform to that order.
  530. now done, see utmpinit (thanks to paul graham)
  531. X: Even better would be to dedicate a file to
  532. X: session information: /etc/stmp, with permanent records in /usr/adm/ttmp.
  533. now done, sesslog/sessnow
  534. X: The association between connections and sessions is still logically
  535. X: separate and belongs in yet another file: say /etc/cstmp and
  536. X: /usr/adm/dttmp. pty would maintain these files along with stmp and ttmp.
  537. now done, sclog/scnow
  538. X
  539. END_OF_FILE
  540. if test 5508 -ne `wc -c <'NOTES'`; then
  541.     echo shar: \"'NOTES'\" unpacked with wrong size!
  542. fi
  543. # end of 'NOTES'
  544. fi
  545. if test -f 'SECURITY' -a "${1}" != "-c" ; then 
  546.   echo shar: Will not clobber existing file \"'SECURITY'\"
  547. else
  548. echo shar: Extracting \"'SECURITY'\" \(5761 characters\)
  549. sed "s/^X//" >'SECURITY' <<'END_OF_FILE'
  550. X    Mode 666 files are always dangerous---they let anyone read or
  551. X    write anything, almost undetectably. ttys are particularly
  552. X    dangerous because the data read from them controls practically
  553. X    all user processes, and because the data written to them
  554. X    controls these weird things called ``humans'' sitting on the
  555. X    other side of the screen.                                ---me
  556. X
  557. An attacker can maintain access to a tty in several ways: (M) having the
  558. master side (say /dev/ptyp6) open; (S) having the slave side (that's
  559. X/dev/ttyp6) open; (T) having /dev/tty open and somehow referring to the
  560. tty; (C) having controlling tty p6. To see whether a pseudo-tty is
  561. secure, pty must test for each of these forms of access.
  562. X
  563. The system must provide several assurances. (1) To gain M, a process
  564. must open /dev/ptyp6 (or receive the descriptor from another process
  565. with M). (2) If any process has M then no process can open /dev/ptyp6.
  566. X(3) To gain S, a process must open /dev/ttyp6 (or receive the descriptor
  567. from another process with S), obeying any protections set up on
  568. X/dev/ttyp6. (4) To gain T, a process must open /dev/tty while it has C
  569. X(or receive the descriptor from another process with T). (5) To gain C,
  570. a process must already have M or S, or be forked from another process
  571. with C, or must open /dev/ttyp6. (6) If a process reads from M, the read
  572. will return 0 (or -1 with errno EIO) if and only if no process has S or
  573. T.
  574. X
  575. pty starts by opening /dev/ptyp6. It keeps /dev/ptyp6 open and never
  576. passes it to another process, so no other process will ever have M.
  577. pty then makes sure that /dev/ttyp6 is owned by its effective uid, and
  578. changes its mode to 0200. Unless it can do this, there is absolutely no
  579. chance of security, and pty won't even try the more powerful tests.
  580. X
  581. If the chmod succeeds, no unprivileged process will be able to open
  582. X/dev/ttyp6. pty then closes any descriptors it had open to /dev/ttyp6,
  583. and reads (in non-blocking mode) from /dev/ptyp6. It calls the pty
  584. insecure if the read returns anything but 0 or -1/EIO. Otherwise, it
  585. knows that at some point in time, no process had S, T, or M (except
  586. itself). It is an easy consequence of the facts stated so far that no
  587. process will ever have or acquire S or M until pty gives up M, changes
  588. the permissions on /dev/ttyp6, or otherwise gives away the show.
  589. X
  590. This level of security is where pty 3.0, expect, and Sun's patched
  591. versions of SunOS 4.1.1 telnetd/rlogind stop. The problem is that some
  592. processes could still have C and thus acquire T. How would you test
  593. whether there are any processes with a certain controlling tty? I've
  594. published two comprehensive solutions up to now: one based on
  595. eliminating C access entirely, one based on using my pff program to look
  596. for SCM access all at once. But there's a simpler (albeit somewhat less
  597. robust and somewhat slower) solution. pty 4.0 simply invokes /bin/ps
  598. cgaxtp6 and parses the output, looking for process IDs other than itself
  599. and that of ps. (It actually counts newlines in the output and error of
  600. ps. If there is some problem invoking ps, there will be zero newlines.
  601. If ps starts and prints any processes with pids other than that of pty
  602. and ps itself, there will be more than one newline. This is safe against
  603. strange process names and poor ps formatting, but it does require the
  604. pids of pty and ps to be positive integers which fit into an int.) On a
  605. Sun, for instance, this extra test takes 0.3 seconds on pty startup.
  606. Note that pty invokes /bin/ps with only the privileges of its real uid.
  607. X
  608. This isn't as robust as I'd like because a process could conceivably
  609. fork, exit in the parent, and have ps miss both processes without even
  610. printing an error message. If it works, though, it guarantees that at
  611. some point in time, no process (other than pty and ps) had S, M, or C
  612. access, and since no process can gain S or M, no process can gain C
  613. either.
  614. X
  615. We're not done yet. Some process might have picked up T access from C
  616. access, then shed C before ps detected it. (I have a test program which
  617. can do this about one time out of twenty; this isn't just a theoretical
  618. concern.) Fortunately, pty can just repeat its first test, detecting any
  619. S or T access. If read() returns 0 or -1/EIO, all S, T, C, and M access
  620. outside of pty is (I believe) completely gone. (pty then chmods and
  621. chowns the tty so that standard tools work.)
  622. X
  623. In retrospect, I don't really like this solution. It's actually rather
  624. portable (given BSD-flavor /bin/ps, that is), but it adds a noticeable
  625. delay at pty startup. I agree with Steve Bellovin that it makes much
  626. more sense to leave a pseudo-tty around (and accounted to the user!)
  627. until the user has closed all descriptors to it. This puts the delay in
  628. the background at the end of a pty session, rather than in the
  629. foreground at the beginning.
  630. X
  631. Life would have been much simpler if there had been a pseudotty() system
  632. call, say pseudotty(fd) int fd[4], which allocated a new tty (just like
  633. a pipe!) and returned master-read master-write slave-read slave-write
  634. descriptors. This would also have solved any problems pseudo-ttys have
  635. with EOF. Even without such a radical change, there's no reason for C or
  636. T access to exist; why can't the current tty be listed in an environment
  637. variable, like TTY=/dev/ttyp6? /dev/tty, controlling ttys, and POSIX
  638. sessions hurt security and make tty-manipulating programs harder to
  639. write. I can't say exactly what the UNIX philosophy is, but that sure
  640. ain't it.
  641. X
  642. Anyway, pty 4.0 works. It fits into current systems and closes every tty
  643. security hole I know. Now I'll put my money where my mouth is. I will
  644. pay $100.00 to the first person to demonstrate that pty 4.0's security
  645. can be defeated. To find out exactly what is required to claim this
  646. prize, send me e-mail.
  647. END_OF_FILE
  648. if test 5761 -ne `wc -c <'SECURITY'`; then
  649.     echo shar: \"'SECURITY'\" unpacked with wrong size!
  650. fi
  651. # end of 'SECURITY'
  652. fi
  653. if test -f 'SYSCONF' -a "${1}" != "-c" ; then 
  654.   echo shar: Will not clobber existing file \"'SYSCONF'\"
  655. else
  656. echo shar: Extracting \"'SYSCONF'\" \(3977 characters\)
  657. sed "s/^X//" >'SYSCONF' <<'END_OF_FILE'
  658. X#!/bin/sh
  659. X# Public domain.
  660. X
  661. X# Configure, eat your heart out.
  662. X
  663. X# make sure we're using sh (stolen from Configure)
  664. PATH='.:/bin:/usr/bin:/usr/local/bin:/usr/ucb:/usr/local:/usr/lbin:/etc'
  665. export PATH || (echo "Aargh! This isn't sh. Desperation time. I will now feed myself to sh..."; exec sh $0; sh $0; kill $$)
  666. echo "We're using sh. Good."
  667. X
  668. X# make sure cmp works
  669. if cmp README README >/dev/null 2>&1
  670. then if cmp README SYSCONF >/dev/null 2>&1
  671. X     then echo 'Aargh! cmp returns a zero exit code for different files. This is hopeless.'
  672. X      exit 1
  673. X     else echo 'You have a normal cmp. Good.'
  674. X     fi
  675. else echo 'Aargh! cmp returns a nonzero exit code for the same file. This is hopeless.'
  676. X     exit 1
  677. fi
  678. X
  679. X# config/posix.h: test for setsid()
  680. cat > .setsid.$$.c << 'YOW'
  681. extern setsid(); main() { setsid(); }
  682. YOW
  683. if cc -o .setsid.$$ .setsid.$$.c >/dev/null 2>&1
  684. then echo 'You have setsid(). I assume this is a POSIX system. Enabling -DPOSIX_SILLINESS...'
  685. X     sed '4s/^#undef POSIX_SILLINESS$/#define POSIX_SILLINESS/' < config/posix.h > config/posix.h.$$
  686. X     cp config/posix.h.$$ config/posix.h
  687. X     rm config/posix.h.$$
  688. else echo 'This is not a POSIX system. Disabling -DPOSIX_SILLINESS...'
  689. X     sed '4s/^#define POSIX_SILLINESS$/#undef POSIX_SILLINESS/' < config/posix.h > config/posix.h.$$
  690. X     cp config/posix.h.$$ config/posix.h
  691. X     rm config/posix.h.$$
  692. fi
  693. rm -f .setsid.$$ .setsid.$$.c
  694. X
  695. X# config/fdsettrouble.h: FD_ZERO
  696. cat > .fdzero.$$.c << 'YOW'
  697. X#include <sys/types.h>
  698. X#include <sys/time.h>
  699. main() { static fd_set rfds; FD_ZERO(&rfds); exit(0); }
  700. YOW
  701. if cc -o .fdzero.$$ .fdzero.$$.c >/dev/null 2>&1
  702. then echo 'You have FD_ZERO. Enabling it...'
  703. X     sed '4s/^#define LACKING_FD_ZERO$/#undef LACKING_FD_ZERO/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
  704. X     cp config/fdsettrouble.h.$$ config/fdsettrouble.h
  705. X     rm config/fdsettrouble.h.$$
  706. else echo 'You do not have FD_ZERO. Disabling it...'
  707. X     sed '4s/^#undef LACKING_FD_ZERO$/#define LACKING_FD_ZERO/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
  708. X     cp config/fdsettrouble.h.$$ config/fdsettrouble.h
  709. X     rm config/fdsettrouble.h.$$
  710. fi
  711. rm -f .fdzero.$$ .fdzero.$$.c
  712. X
  713. X# config/fdsettrouble.h: fd_set
  714. cat > .fdset.$$.c << 'YOW'
  715. X#include <sys/types.h>
  716. X#include <sys/time.h>
  717. main() { static fd_set rfds; exit(0); }
  718. YOW
  719. if cc -o .fdset.$$ .fdset.$$.c >/dev/null 2>&1
  720. then echo 'You have fd_set. Enabling it...'
  721. X     sed '5s/^#define DESPERATE_FD_SET$/#undef DESPERATE_FD_SET/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
  722. X     cp config/fdsettrouble.h.$$ config/fdsettrouble.h
  723. X     rm config/fdsettrouble.h.$$
  724. else echo 'You do not have fd_set. Disabling it...'
  725. X     sed '5s/^#undef DESPERATE_FD_SET$/#define DESPERATE_FD_SET/' < config/fdsettrouble.h > config/fdsettrouble.h.$$
  726. X     cp config/fdsettrouble.h.$$ config/fdsettrouble.h
  727. X     rm config/fdsettrouble.h.$$
  728. fi
  729. rm -f .fdset.$$ .fdset.$$.c
  730. X
  731. X# config/ttyopts.h: TTY_WINDOWS
  732. cat > .winch.$$.c << 'YOW'
  733. X#include <signal.h>
  734. main() { int winch = SIGWINCH; exit(0); }
  735. YOW
  736. if cc -o .winch.$$ .winch.$$.c >/dev/null 2>&1
  737. then echo 'You have tty windows. Enabling them...'
  738. X     sed '4s/^#undef TTY_WINDOWS$/#define TTY_WINDOWS/' < config/ttyopts.h > config/ttyopts.h.$$
  739. X     cp config/ttyopts.h.$$ config/ttyopts.h
  740. X     rm config/ttyopts.h.$$
  741. else echo 'You do not have tty windows. Disabling them... (I guess this is BSD 4.2.)'
  742. X     sed '4s/^#define TTY_WINDOWS$/#undef TTY_WINDOWS/' < config/ttyopts.h > config/ttyopts.h.$$
  743. X     cp config/ttyopts.h.$$ config/ttyopts.h
  744. X     rm config/ttyopts.h.$$
  745. fi
  746. rm -f .winch.$$ .winch.$$.c
  747. X
  748. echo ' '
  749. echo 'Okay, now you should make CHECKCONF and run it.'
  750. echo 'If you would like me to do this for you, press return.'
  751. echo 'Otherwise type no and press return, or just interrupt this script.'
  752. read foo
  753. case x"$foo"y in
  754. xy) echo 'make CHECKCONF'
  755. X    if make CHECKCONF
  756. X    then echo './CHECKCONF'
  757. X     ./CHECKCONF
  758. X    else echo 'Oops, make CHECKCONF failed. I give up: you figure it out.'
  759. X    fi
  760. X    ;;
  761. esac
  762. X
  763. exit 0
  764. END_OF_FILE
  765. if test 3977 -ne `wc -c <'SYSCONF'`; then
  766.     echo shar: \"'SYSCONF'\" unpacked with wrong size!
  767. fi
  768. chmod +x 'SYSCONF'
  769. # end of 'SYSCONF'
  770. fi
  771. if test -f 'TESTS' -a "${1}" != "-c" ; then 
  772.   echo shar: Will not clobber existing file \"'TESTS'\"
  773. else
  774. echo shar: Extracting \"'TESTS'\" \(5634 characters\)
  775. sed "s/^X//" >'TESTS' <<'END_OF_FILE'
  776. Typescript of a typical pty test run. EOF indicated by [^D]. Beeps
  777. indicated by ^G.
  778. X
  779. Script started on Fri Dec 20 01:17:45 PST 1991
  780. csh% who
  781. bernstei ttyp0   Dec 20 01:15   (annex2.Berkeley.)
  782. bernstei ttyq3   Dec 20 01:17   (script)
  783. csh% /bin/who
  784. bernstei ttyp0   Dec 20 01:15   (annex2.Berkeley.)
  785. bernstei ttyq3   Dec 20 01:17   (script)
  786. csh% users
  787. bernstei bernstei 
  788. csh% /usr/ucb/users
  789. bernstei bernstei
  790. csh% tty
  791. X/dev/ttyq3
  792. csh% /bin/tty
  793. X/dev/ttyq3
  794. csh% who am i
  795. fred!bernstei ttyq3   Dec 20 01:17 (script)
  796. csh% /bin/who am i
  797. fred!bernstei ttyq3   Dec 20 01:17 (script)
  798. csh% who am i < /dev/tty
  799. csh% /bin/who am i < /dev/tty
  800. csh% who am i < /dev/null
  801. who: fatal: stdin not a tty
  802. csh% /bin/who am i < /dev/null
  803. bernstei tty??   Dec 20 01:18
  804. csh% whoami
  805. bernstei
  806. csh% /usr/ucb/whoami
  807. bernstei
  808. csh% echo hi > /dev/tty
  809. hi
  810. csh% echo hi > /dev/ttyq3
  811. hi
  812. csh% exclon
  813. csh% echo hi > /dev/tty
  814. X/dev/tty: Device busy
  815. csh% echo hi > /dev/ttyq3
  816. X/dev/ttyq3: Device busy
  817. csh% excloff
  818. csh% argv0 /usr/lib/sendmail mailq
  819. X                Mail Queue (1 request)
  820. X--QID-- -Size- ----Q-Time----- ------------Sender/Recipient------------
  821. AA09963*  (no control file)
  822. csh% lock
  823. Key: 
  824. Again: 
  825. Bad password!
  826. X^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^G^Gcsh% lock
  827. Key: 
  828. Again: 
  829. X^Gcsh% biff
  830. is n
  831. csh% mesg
  832. is y
  833. csh% pty pwd
  834. X/tmp/pty
  835. csh% pty who
  836. bernstei ttyp0   Dec 20 01:15   (annex2.Berkeley.)
  837. bernstei ttyq3   Dec 20 01:17   (script)
  838. csh% pty pty pty pty pty pwd
  839. X/tmp/pty
  840. csh% time pty pty pty pty pty pty pty pty pty pty pty pty pty pty pty pty pwd
  841. X/tmp/pty
  842. X0.0u 0.3s 0:21 1% 0+104k 0+0io 5pf+0w
  843. csh% pty -xu who
  844. bernstei ttyp0   Dec 20 01:15   (annex2.Berkeley.)
  845. bernstei ttyq3   Dec 20 01:17   (script)
  846. bernstei ttyr4   Dec 20 01:23   (pty4.0)
  847. csh% pty -xu pty -xu pty -xu who
  848. bernstei ttyp0   Dec 20 01:15   (annex2.Berkeley.)
  849. bernstei ttyq3   Dec 20 01:17   (script)
  850. bernstei ttyr7   Dec 20 01:23   (pty4.0)
  851. bernstei ttyr9   Dec 20 01:23   (pty4.0)
  852. bernstei ttyrb   Dec 20 01:23   (pty4.0)
  853. csh% pty -xRu pty -xRu pty -xRu who
  854. bernstei ttyp0   Dec 20 01:15   (annex2.Berkeley.)
  855. bernstei ttyp2   Dec 20 01:23   (pty4.0)
  856. bernstei ttyp3   Dec 20 01:23   (pty4.0)
  857. bernstei ttyp4   Dec 20 01:23   (pty4.0)
  858. bernstei ttyq3   Dec 20 01:17   (script)
  859. csh% pty -xu -h random who
  860. bernstei ttyp0   Dec 20 01:15   (annex2.Berkeley.)
  861. bernstei ttyq3   Dec 20 01:17   (script)
  862. bernstei ttyrb   Dec 20 01:24   (random)
  863. csh% tty
  864. X/dev/ttyq3
  865. csh% ttydetach sh
  866. X$ echo $$
  867. X2138
  868. X$ ps 2138
  869. X  PID TT STAT  TIME COMMAND
  870. X 2138 ?  S     0:00 sh
  871. X$ [^D] csh% ttydetach pty -d pwd
  872. X/tmp/pty
  873. csh% pty -d pwd
  874. X/tmp/pty
  875. csh% tr '[A-Z]' '[a-z]' | cat -t
  876. one line of input
  877. another
  878. and yet another
  879. why no output?
  880. X[^D] one line of input
  881. another
  882. and yet another
  883. why no output?
  884. csh% nobuf tr '[A-Z]' '[a-z]' | cat -t
  885. one line of input
  886. one line of input
  887. ah, instant response!
  888. ah, instant response!
  889. the joy of buffer control!
  890. the joy of buffer control!
  891. X[^D] csh% sess sh
  892. X$ echo $PTY
  893. r7
  894. X$ sesslist
  895. session r7 pid 2174 slave 2175 connected
  896. X$ sesswho
  897. r7  Dec 20 01:29  bernstei          2174
  898. X$ sesswhere
  899. r7  Dec 20 01:29  connect (unknown)
  900. X$ disconnect
  901. csh% sesslist
  902. session r7 pid 2174 slave 2175 disconnected
  903. csh% sesswho
  904. r7  Dec 20 01:29  bernstei          2174
  905. csh% sess sh
  906. X$ sesslist
  907. session r7 pid 2174 slave 2175 disconnected
  908. session qf pid 2187 slave 2188 connected
  909. X$ sessname 'foo-prompt'
  910. X$ PS1=foo-prompt' '
  911. foo-prompt sesslist
  912. session r7 pid 2174 slave 2175 disconnected
  913. session qf pid 2187 slave 2188 connected: foo-prompt
  914. foo-prompt sessname
  915. session qf: foo-prompt
  916. foo-prompt reconnect r7
  917. reconnect: will connect to session r7 when session qf is done
  918. foo-prompt disconnect
  919. pty: info: reconnecting to r7
  920. pty: info: successfully connected to r7
  921. X$ 
  922. X$ reconnect qf
  923. reconnect: will connect to session qf when session r7 is done
  924. X$ disconnect
  925. pty: info: reconnecting to qf
  926. pty: info: successfully connected to qf
  927. foo-prompt reconnect r7
  928. reconnect: will connect to session r7 when session qf is done
  929. foo-prompt disconnect
  930. pty: info: reconnecting to r7
  931. pty: info: successfully connected to r7
  932. X$ reconnect qf
  933. reconnect: will connect to session qf when session r7 is done
  934. X$ sesslist
  935. session r7 pid 2174 slave 2175 connected (will drop into qf)
  936. session qf pid 2187 slave 2188 disconnected: foo-prompt
  937. X$ [^D] pty: info: reconnecting to qf
  938. pty: info: successfully connected to qf
  939. foo-prompt sesslist
  940. session qf pid 2187 slave 2188 connected: foo-prompt
  941. foo-prompt sesswhere
  942. qf  Dec 20 01:32  connect (unknown)
  943. foo-prompt sesswho
  944. qf  Dec 20 01:31  bernstei          2187
  945. foo-prompt sesslist
  946. session qf pid 2187 slave 2188 connected: foo-prompt
  947. foo-prompt sesskill -mysqf
  948. csh% set notify
  949. csh% pty sh
  950. X$ ^Z
  951. Stopped
  952. csh% bg
  953. X[1]    pty sh &
  954. X
  955. X[1]  + Stopped (tty output) pty sh
  956. csh% bg
  957. X[1]    pty sh &
  958. X
  959. X[1]  + Stopped (tty output) pty sh
  960. csh% bg
  961. X[1]    pty sh &
  962. X
  963. X[1]  + Stopped (tty output) pty sh
  964. csh% bg
  965. X[1]    pty sh &
  966. csh% bg
  967. X[1]  + Stopped (tty output) pty sh
  968. X
  969. X[1]    pty sh &
  970. csh% bg
  971. bg: No current job.
  972. csh% bg
  973. bg: No current job.
  974. X
  975. X[1]  + Stopped (tty output) pty sh
  976. csh% bg
  977. X[1]    pty sh &
  978. csh% 
  979. X[1]  + Stopped (tty output) pty sh
  980. bg
  981. X[1]    pty sh &
  982. csh% 
  983. X[1]  + Stopped (tty output) pty sh
  984. bg;bg;bg;
  985. X[1]    pty sh &
  986. X
  987. X[1]  + Stopped (tty output) pty sh
  988. X[1]    pty sh &
  989. bg: No current job.
  990. csh% 
  991. X[1]  + Stopped (tty output) pty sh
  992. X
  993. csh% fg
  994. pty sh
  995. X
  996. X$ 
  997. X$ [^D] csh% pty sh &
  998. X[1] 2224
  999. csh% 
  1000. X[1]  + Stopped (tty output) pty sh
  1001. X
  1002. csh% bg
  1003. X[1]    pty sh &
  1004. X
  1005. X[1]  + Stopped (tty output) pty sh
  1006. csh% bg
  1007. X[1]    pty sh &
  1008. X
  1009. X[1]  + Stopped (tty output) pty sh
  1010. csh% fg
  1011. pty sh
  1012. X$ 
  1013. X$ 
  1014. X$ 
  1015. X$ [^D] csh% pty sh
  1016. X$ echo $PTY
  1017. r6
  1018. X$ ttydetach sh
  1019. X$ echo $PTY
  1020. X
  1021. X$ [^D] $ [^D] csh% jobs
  1022. csh% [^D] Script done on Fri Dec 20 01:40:01 PST 1991
  1023. END_OF_FILE
  1024. if test 5634 -ne `wc -c <'TESTS'`; then
  1025.     echo shar: \"'TESTS'\" unpacked with wrong size!
  1026. fi
  1027. # end of 'TESTS'
  1028. fi
  1029. if test -f 'checkptys.c' -a "${1}" != "-c" ; then 
  1030.   echo shar: Will not clobber existing file \"'checkptys.c'\"
  1031. else
  1032. echo shar: Extracting \"'checkptys.c'\" \(5995 characters\)
  1033. sed "s/^X//" >'checkptys.c' <<'END_OF_FILE'
  1034. X#include <stdio.h>
  1035. X#include <sys/types.h>
  1036. X#include <sys/stat.h>
  1037. X#include <sys/file.h>
  1038. X#include "config/devmty.h"
  1039. X#include "config/devsty.h"
  1040. X#include "config/posix.h" /* XXX: why? */
  1041. X#include "config/ptybin.h"
  1042. X#include "config/ptydir.h"
  1043. X#include "config/ptyext.h"
  1044. X#include "config/ptygroup.h"
  1045. X#include "config/ptymodes.h"
  1046. X#include "config/ptyopts.h"
  1047. X#include "config/sessconnfile.h"
  1048. X#include "config/sessfile.h"
  1049. X#include <utmp.h>
  1050. X#include "config/utmpfile.h"
  1051. X#include "config/wtmpfile.h"
  1052. X
  1053. char ptybin[] = PTYBIN;
  1054. char ptydir[] = PTYDIR;
  1055. char sessconnnow[] = SESSCONNNOW_FILE;
  1056. char sessconnlog[] = SESSCONNLOG_FILE;
  1057. char sessnow[] = SESSNOW_FILE;
  1058. char sesslog[] = SESSLOG_FILE;
  1059. char utmp[] = UTMP_FILE;
  1060. char wtmp[] = WTMP_FILE;
  1061. X
  1062. char devmty[sizeof(DEVMTY) + 5] = DEVMTY;
  1063. char devsty[sizeof(DEVSTY) + 5] = DEVSTY;
  1064. char pty1[] = PTYEXT1;
  1065. char pty2[] = PTYEXT2;
  1066. X
  1067. void aack(s,t)
  1068. char *s;
  1069. char *t;
  1070. X{
  1071. X printf("aack! %s: %s\n",s,t);
  1072. X}
  1073. X
  1074. void checkbin(s,level)
  1075. char *s;
  1076. int level; /* 0 script 1 normal 2 setgid 3 setuid */
  1077. X{
  1078. X struct stat st;
  1079. X
  1080. X if (stat(s,&st) == -1)
  1081. X  {
  1082. X   aack("executable does not exist",s);
  1083. X   return;
  1084. X  }
  1085. X if (!(st.st_mode & 001))
  1086. X   aack("program isn't world-executable",s);
  1087. X switch(st.st_mode & 06000)
  1088. X  {
  1089. X   case 0:
  1090. X     if (level == 2)
  1091. X       aack("program should be setgid but isn't",s);
  1092. X     if (level == 3)
  1093. X       aack("program should be setuid but isn't",s);
  1094. X     break;
  1095. X   case 06000:
  1096. X   case 04000:
  1097. X     if (level == 0)
  1098. X       aack("SHELL SCRIPT IS SETUID! FIX IMMEDIATELY!",s);
  1099. X     if (level == 1)
  1100. X       aack("program is SETUID, shouldn't be",s);
  1101. X     if (level == 2)
  1102. X       aack("program is SETUID, should be setgid instead",s);
  1103. X     break;
  1104. X   case 02000:
  1105. X     if (level == 0)
  1106. X       aack("SHELL SCRIPT IS SETGID! FIX IMMEDIATELY!",s);
  1107. X     if (level == 1)
  1108. X       aack("program is setgid, shouldn't be",s);
  1109. X     if (level == 3)
  1110. X       aack("program is setgid, should be setuid instead",s);
  1111. X     break;
  1112. X   default:
  1113. X     aack("computer doesn't understand binary arithmetic",s);
  1114. X  }
  1115. X if (st.st_mode & 002)
  1116. X   aack("program is WORLD-writable",s);
  1117. X if ((level == 2) && (st.st_gid != PTYGROUP))
  1118. X   aack("program should be setgid to tty group but isn't",s);
  1119. X}
  1120. X
  1121. void testpath(s)
  1122. char *s;
  1123. X{
  1124. X char *t;
  1125. X struct stat st;
  1126. X char old;
  1127. X
  1128. X if (*s != '/')
  1129. X  {
  1130. X   aack("path does not start with a slash",s);
  1131. X   return;
  1132. X  }
  1133. X t = s + 1;
  1134. X for (;;)
  1135. X  {
  1136. X   if ((*t == '/') || (*t == 0))
  1137. X    {
  1138. X     old = *t;
  1139. X     *t = 0;
  1140. X     if (stat(*s ? s : "/",&st) == -1)
  1141. X       aack("cannot stat component of path",*s ? s : "/");
  1142. X     else
  1143. X      {
  1144. X       if (st.st_mode & 022)
  1145. X     aack("component directory is WORLD- and group-writable",*s ? s : "/");
  1146. X       else if (st.st_mode & 002)
  1147. X     aack("component directory is WORLD-writable",*s ? s : "/");
  1148. X       else if (st.st_mode & 020)
  1149. X     aack("component directory is group-writable",*s ? s : "/");
  1150. X      }
  1151. X     *t = old;
  1152. X    }
  1153. X   if (!*t)
  1154. X     break;
  1155. X   ++t;
  1156. X  }
  1157. X}
  1158. X
  1159. void testpty(fnm,fns,unusedptyowner)
  1160. char *fnm;
  1161. char *fns;
  1162. int unusedptyowner;
  1163. X{
  1164. X struct stat stm;
  1165. X struct stat sts;
  1166. X int fdm;
  1167. X
  1168. X if (stat(fnm,&stm) == -1)
  1169. X   return;
  1170. X if (stat(fns,&sts) == -1)
  1171. X  {
  1172. X   aack("pty master has no corresponding slave",fnm);
  1173. X   return;
  1174. X  }
  1175. X
  1176. X fdm = open(fnm,O_RDWR);
  1177. X if (stm.st_mode & 0777 != 0666)
  1178. X  {
  1179. X   aack("pty master has weird mode",fnm);
  1180. X  }
  1181. X if (fdm == -1)
  1182. X  {
  1183. X   printf("pty master %s in use, slave owned by uid %d\n",fnm,sts.st_uid);
  1184. X   if (sts.st_mode & 006)
  1185. X     aack("pty slave allows WORLD access",fns);
  1186. X   if (sts.st_gid != PTYGROUP)
  1187. X     aack("pty slave group does not match standard tty group",fns);
  1188. X   if (sts.st_mode & 020)
  1189. X     printf("pty slave %s messages on\n",fns);
  1190. X   if (sts.st_mode & 0100)
  1191. X     printf("pty slave %s biff on\n",fns);
  1192. X  }
  1193. X else
  1194. X  {
  1195. X   if (sts.st_uid != unusedptyowner)
  1196. X     aack("unused pty slave not owned by standard unused tty owner",fns);
  1197. X   close(fdm);
  1198. X  }
  1199. X}
  1200. X
  1201. main()
  1202. X{
  1203. X int p1;
  1204. X int p2;
  1205. X
  1206. X printf("Testing main pty directory %s...\n",ptydir);
  1207. X testpath(ptydir);
  1208. X printf("Testing utmp file %s...\n",utmp);
  1209. X testpath(utmp);
  1210. X printf("Testing wtmp file %s...\n",wtmp);
  1211. X testpath(wtmp);
  1212. X printf("Testing session log file %s...\n",sesslog);
  1213. X testpath(sesslog);
  1214. X printf("Testing current session file %s...\n",sessnow);
  1215. X testpath(sessnow);
  1216. X printf("Testing session-connection log file %s...\n",sessconnlog);
  1217. X testpath(sessconnlog);
  1218. X printf("Testing current session-connection file %s...\n",sessconnnow);
  1219. X testpath(sessconnnow);
  1220. X printf("Testing pty binary directory %s...\n",ptybin);
  1221. X testpath(ptybin);
  1222. X
  1223. X for (p1 = 0;pty1[p1];++p1)
  1224. X   for (p2 = 0;pty2[p2];++p2)
  1225. X    {
  1226. X     devmty[sizeof(DEVMTY) - 1] = pty1[p1];
  1227. X     devmty[sizeof(DEVMTY)] = pty2[p2];
  1228. X     devsty[sizeof(DEVSTY) - 1] = pty1[p1];
  1229. X     devsty[sizeof(DEVSTY)] = pty2[p2];
  1230. X     testpty(devmty,devsty,geteuid());
  1231. X    }
  1232. X
  1233. X printf("Checking for actual pty-related binaries in %s...\n",ptybin);
  1234. X if (chdir(ptybin) == -1)
  1235. X  {
  1236. X   aack("cannot switch to pty binary directory",ptybin);
  1237. X  }
  1238. X else
  1239. X  {
  1240. X   checkbin("argv0",1);
  1241. X   checkbin("biff",1);
  1242. X   checkbin("checkptys",1);
  1243. X   checkbin("condom",0);
  1244. X   checkbin("ctrlv",1);
  1245. X   checkbin("disconnect",3);
  1246. X   checkbin("excloff",1);
  1247. X   checkbin("exclon",1);
  1248. X   checkbin("lock",1);
  1249. X   checkbin("mesg",1);
  1250. X   checkbin("nobuf",0);
  1251. X   checkbin("pty",3);
  1252. X   checkbin("reconnect",3);
  1253. X   checkbin("script",0);
  1254. X   checkbin("script.tidy",0);
  1255. X   checkbin("sess",0);
  1256. X   checkbin("sesskill",3);
  1257. X   checkbin("sesslist",3);
  1258. X   checkbin("sessmenu",1);
  1259. X   checkbin("sessname",3);
  1260. X   checkbin("sesswhere",1);
  1261. X   checkbin("sesswho",1);
  1262. X   checkbin("tiocsti",1);
  1263. X   checkbin("tplay",1);
  1264. X   checkbin("trecord",1);
  1265. X   checkbin("tscript",0);
  1266. X   checkbin("tty",1);
  1267. X   checkbin("ttydetach",1);
  1268. X   checkbin("ttyprotect",0);
  1269. X   checkbin("users",1);
  1270. X   checkbin("utmpinit",1);
  1271. X   checkbin("waitfor",1);
  1272. X   checkbin("wall",2);
  1273. X   checkbin("who",1);
  1274. X   checkbin("whoami",1);
  1275. X   checkbin("write",2);
  1276. X   checkbin("wtmprotate",0);
  1277. X   checkbin("sessrotate",0);
  1278. X   checkbin("sclogrotate",0);
  1279. X   checkbin("sessnowinit",0);
  1280. X   checkbin("scnowinit",0);
  1281. X  }
  1282. X
  1283. X exit(0);
  1284. X}
  1285. END_OF_FILE
  1286. if test 5995 -ne `wc -c <'checkptys.c'`; then
  1287.     echo shar: \"'checkptys.c'\" unpacked with wrong size!
  1288. fi
  1289. # end of 'checkptys.c'
  1290. fi
  1291. if test -f 'env.c' -a "${1}" != "-c" ; then 
  1292.   echo shar: Will not clobber existing file \"'env.c'\"
  1293. else
  1294. echo shar: Extracting \"'env.c'\" \(4098 characters\)
  1295. sed "s/^X//" >'env.c' <<'END_OF_FILE'
  1296. X/* env.c, env.h: environ library
  1297. Daniel J. Bernstein, brnstnd@nyu.edu.
  1298. Depends on ralloc.h.
  1299. Requires strlen, strncmp, environ.
  1300. X8/3/91: Fixed off-by-one [sigh] in env_put2().
  1301. X7/24/91: Added env_put2(). Recoded in terms of ralloc macros.
  1302. X7/18/91: Cleanups. env 1.1, public domain.
  1303. X7/10/91: Was ralloc()ing too little; didn't init env right. Tnx CW/EW/HB.
  1304. X6/29/91: Added env_unsetlen(), made env_add use it so string can be const.
  1305. X6/28/91: Baseline. env 1.0, public domain.
  1306. No known patent problems.
  1307. X
  1308. Thanks to Christian Wettergren <d88-cwe@pdc.kth.se>, Erik Wallin
  1309. X<d87-ewa@pdc.kth.se>, and Harald Barth <d88-hba@tds.kth.se> for bug
  1310. fixes.
  1311. X
  1312. This was originally meant as a portable version of putenv(). It expanded
  1313. to include unsetenv() and getenv(). Note that the routines always
  1314. maintain environ properly, so execvp() and friends will pick up the new
  1315. variables. I recommend that programs which do a lot of environment
  1316. manipulation work with strings and keep their own hash table, then use
  1317. these routines to manipulate environ before an execvp().
  1318. X
  1319. env_init() does optional initialization. It returns 0 on success, -1 on
  1320. failure. Note that env_init(), env_put(), and env_unset() may all change
  1321. environ.
  1322. X
  1323. env_put("FOO=BAR") adds FOO=BAR to the environment, destroying any
  1324. previous value of FOO. It returns 0 on success, -1 on failure. Note that
  1325. previous versions of env.c required env_put's argument to be writable;
  1326. this problem has been removed.
  1327. X
  1328. env_put2("FOO","BAR") is just like env_put("FOO=BAR"), except of course
  1329. that in the second case the new environment variable refers to the
  1330. string passed to env_put, while in the first case it refers to
  1331. internally malloc()ed memory.
  1332. X
  1333. env_unset("FOO") unsets any variable FOO. It returns 0 on success, -1 on
  1334. failure. It will always succeed if env_init() has previously succeeded.
  1335. X
  1336. env_get("FOO") returns the value of the first variable FOO, or 0 if
  1337. there is no such variable.
  1338. X
  1339. env_pick() returns any FOO=BAR in the environment, or 0 if the
  1340. environment is empty. This can be used to implement the BSD printenv
  1341. call, or to clear the environment.
  1342. X*/
  1343. X
  1344. X#include "env.h"
  1345. X#include "ralloc.h"
  1346. X
  1347. static int init = 0;
  1348. static int numenv;
  1349. static int allocenv;
  1350. X
  1351. extern char *env_get(s)
  1352. char *s;
  1353. X{
  1354. X int i;
  1355. X int slen;
  1356. X char *envi;
  1357. X
  1358. X slen = strlen(s);
  1359. X for (i = 0;envi = environ[i];++i)
  1360. X   if ((!strncmp(s,envi,slen)) && (envi[slen] == '='))
  1361. X     return envi + slen + 1;
  1362. X return 0;
  1363. X}
  1364. X
  1365. extern char *env_pick()
  1366. X{
  1367. X return environ[0]; /* environ[numenv-1] would make (pick-unset)^n easier */
  1368. X}
  1369. X
  1370. static void env_unsetlen(s,slen)
  1371. char *s;
  1372. int slen;
  1373. X{
  1374. X int i;
  1375. X for (i = 0;i < numenv;++i)
  1376. X   if ((!strncmp(s,environ[i],slen)) && (environ[i][slen] == '='))
  1377. X    {
  1378. X     if (i < --numenv)
  1379. X       environ[i] = environ[numenv];
  1380. X     environ[numenv] = 0;
  1381. X    }
  1382. X}
  1383. X
  1384. extern int env_unset(s)
  1385. char *s;
  1386. X{
  1387. X if (!init)
  1388. X   if (env_init())
  1389. X     return -1;
  1390. X env_unsetlen(s,strlen(s));
  1391. X return 0;
  1392. X}
  1393. X
  1394. static int env_realloc()
  1395. X{
  1396. X char **envp;
  1397. X
  1398. X allocenv = numenv + 30;
  1399. X envp = environ;
  1400. X environ = RALLOC(char *,allocenv + 1);
  1401. X if (!environ)
  1402. X  {
  1403. X   environ = envp;
  1404. X   allocenv = numenv;
  1405. X   return -1;
  1406. X  }
  1407. X numenv = 0;
  1408. X while (*envp)
  1409. X  {
  1410. X   environ[numenv] = *envp;
  1411. X   ++numenv;
  1412. X   ++envp;
  1413. X  }
  1414. X environ[numenv] = 0;
  1415. X RFREE(envp - numenv);
  1416. X return 0;
  1417. X}
  1418. X
  1419. static int env_add(s)
  1420. char *s;
  1421. X{
  1422. X char *t;
  1423. X for (t = s;*t;++t)
  1424. X   if (*t == '=')
  1425. X    {
  1426. X     env_unsetlen(s,t - s);
  1427. X     break;
  1428. X    }
  1429. X if (numenv == allocenv)
  1430. X   if (env_realloc())
  1431. X     return -1;
  1432. X environ[numenv] = s;
  1433. X ++numenv;
  1434. X environ[numenv] = 0;
  1435. X return 0;
  1436. X}
  1437. X
  1438. int env_init()
  1439. X{
  1440. X char **envp;
  1441. X
  1442. X numenv = 0;
  1443. X allocenv = 0;
  1444. X envp = environ;
  1445. X environ = RALLOC(char *,1);
  1446. X if (!environ)
  1447. X  {
  1448. X   environ = envp;
  1449. X   return -1;
  1450. X  }
  1451. X environ[0] = 0;
  1452. X init = 1;
  1453. X while (*envp)
  1454. X  {
  1455. X   if (env_add(*envp))
  1456. X     return -1;
  1457. X   ++envp;
  1458. X  }
  1459. X return 0;
  1460. X}
  1461. X
  1462. int env_put(s)
  1463. char *s;
  1464. X{
  1465. X if (!init)
  1466. X   if (env_init())
  1467. X     return -1;
  1468. X return env_add(s);
  1469. X}
  1470. X
  1471. int env_put2(s,t)
  1472. char *s;
  1473. char *t;
  1474. X{
  1475. X char *u;
  1476. X int slen;
  1477. X slen = strlen(s);
  1478. X u = ralloc(slen + strlen(t) + 2);
  1479. X if (!u)
  1480. X   return -1;
  1481. X strcpy(u,s);
  1482. X u[slen] = '=';
  1483. X strcpy(u + slen + 1,t);
  1484. X return env_put(u);
  1485. X}
  1486. END_OF_FILE
  1487. if test 4098 -ne `wc -c <'env.c'`; then
  1488.     echo shar: \"'env.c'\" unpacked with wrong size!
  1489. fi
  1490. # end of 'env.c'
  1491. fi
  1492. if test -f 'fmt.c' -a "${1}" != "-c" ; then 
  1493.   echo shar: Will not clobber existing file \"'fmt.c'\"
  1494. else
  1495. echo shar: Extracting \"'fmt.c'\" \(5780 characters\)
  1496. sed "s/^X//" >'fmt.c' <<'END_OF_FILE'
  1497. X/* fmt.c, fmt.h: formatting library
  1498. Daniel J. Bernstein, brnstnd@nyu.edu.
  1499. No dependencies.
  1500. No environment requirements.
  1501. X10/5/91: Cleaned up fmt_rvis, added fmt_unrvis.
  1502. X9/1/91: Added fmt_nvis, fmt_rvis.
  1503. X8/28/91: Added fmt_vis.
  1504. X7/18/91: Baseline. fmt 1.0, public domain.
  1505. No known patent problems.
  1506. X
  1507. XXXX: still need floating-point formatting
  1508. X
  1509. X*/
  1510. X
  1511. X#include "fmt.h"
  1512. X
  1513. X/* To find out the actual length of the formatted value, pass a first
  1514. argument of (char *) 0. */
  1515. X
  1516. X#define zero '0'
  1517. X#define alow 'a'
  1518. X#define plus '+'
  1519. X#define minus '-'
  1520. X#define xlow 'x'
  1521. X
  1522. unsigned int fmt_ulong(s,u) char *s; unsigned long u;
  1523. X{
  1524. X unsigned int len; unsigned long q;
  1525. X len = 1; q = u;
  1526. X while (q > 9) { ++len; q /= 10; }
  1527. X if (s)
  1528. X  {
  1529. X   s += len;
  1530. X   do { *--s = zero + (u % 10); u /= 10; } while(u); /* handles u == 0 */
  1531. X  }
  1532. X return len;
  1533. X}
  1534. X
  1535. unsigned int fmt_xlong(s,u) char *s; unsigned long u;
  1536. X{
  1537. X unsigned int len; unsigned long q; unsigned long c;
  1538. X len = 1; q = u;
  1539. X while (q > 15) { ++len; q /= 16; }
  1540. X if (s)
  1541. X  {
  1542. X   s += len;
  1543. X   do { c = u & 15; *--s = (c > 9 ? alow - 10 : zero) + c; u /= 16; } while(u);
  1544. X  }
  1545. X return len;
  1546. X}
  1547. X
  1548. unsigned int fmt_nbblong(s,n,base,bext,u)
  1549. char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned long u;
  1550. X/* I hope this meaning of n (min, not max) doesn't hurt anything. */
  1551. X{
  1552. X unsigned int len; unsigned long q; unsigned long c;
  1553. X len = 1; q = u; bext += base;
  1554. X while (q > bext - 1) { ++len; q /= bext; } if (len < n) len = n;
  1555. X if (s)
  1556. X  {
  1557. X   s += len;
  1558. X   do { c = u % bext; *--s = (c >= base ? alow - base : zero) + c; u /= bext; }
  1559. X   while(u);
  1560. X  }
  1561. X return len;
  1562. X}
  1563. X
  1564. unsigned int fmt_ushort(s,u) char *s; unsigned short u;
  1565. X{
  1566. X unsigned long l; l = u; return fmt_ulong(s,l);
  1567. X}
  1568. X
  1569. unsigned int fmt_xshort(s,u) char *s; unsigned short u;
  1570. X{
  1571. X unsigned long l; l = u; return fmt_xlong(s,l);
  1572. X}
  1573. X
  1574. unsigned int fmt_nbbshort(s,n,base,bext,u)
  1575. char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned short u;
  1576. X{
  1577. X unsigned long l; l = u; return fmt_nbblong(s,n,base,bext,l);
  1578. X}
  1579. X
  1580. unsigned int fmt_uint(s,u) char *s; unsigned int u;
  1581. X{
  1582. X unsigned long l; l = u; return fmt_ulong(s,l);
  1583. X}
  1584. X
  1585. unsigned int fmt_xint(s,u) char *s; unsigned int u;
  1586. X{
  1587. X unsigned long l; l = u; return fmt_xlong(s,l);
  1588. X}
  1589. X
  1590. unsigned int fmt_nbbint(s,n,base,bext,u)
  1591. char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned int u;
  1592. X{
  1593. X unsigned long l; l = u; return fmt_nbblong(s,n,base,bext,l);
  1594. X}
  1595. X
  1596. unsigned int fmt_plusminus(s,sign) char *s; int sign;
  1597. X{
  1598. X if (s) *s = ((sign < 0) ? minus : plus); return 1;
  1599. X}
  1600. X
  1601. unsigned int fmt_minus(s,sign) char *s; int sign;
  1602. X{
  1603. X if (sign > 0) return 0;
  1604. X if (s) *s = minus; return 1;
  1605. X}
  1606. X
  1607. unsigned int fmt_0x(s,base) char *s; int base;
  1608. X{
  1609. X if (base == 10) return 0;
  1610. X if (s) *s = zero; if (base == 8) return 1;
  1611. X if (s) s[1] = xlow; return 2;
  1612. X}
  1613. X
  1614. unsigned int fmt_strncpy(s,t,n) char *s; char *t; unsigned int n;
  1615. X{
  1616. X unsigned int len;
  1617. X len = 0;
  1618. X if (s) { while (s[len] = t[len]) if (++len == n) break; }
  1619. X else { while (t[len]) if (++len == n) break; }
  1620. X return len;
  1621. X}
  1622. X
  1623. unsigned int fmt_memcpy(s,t,n) char *s; char *t; unsigned int n;
  1624. X/* This and fmt_vis are the only functions where n == 0 means do nothing. */
  1625. X{
  1626. X unsigned int len;
  1627. X if (s)
  1628. X   for (len = 0;len < n;++len)
  1629. X     s[len] = t[len];
  1630. X return n;
  1631. X}
  1632. X
  1633. static unsigned int fmt_xvis(s,t,n,x) char *s; char *t; unsigned int n; int x;
  1634. X{
  1635. X unsigned int len;
  1636. X for (len = 0;n;--n,++t)
  1637. X  {
  1638. X   int ch;
  1639. X   ch = (int) (unsigned int) (unsigned char) *t;
  1640. X   /* XXX: ASCII dependent! */
  1641. X   if (ch > 127)
  1642. X    { if (s) { s[len] = 'M'; s[len + 1] = '-'; } len += 2; ch -= 128; }
  1643. X   if (((ch >= 32) && (ch <= 126)) || (ch == x))
  1644. X    { if (s) s[len] = ch; ++len; continue; }
  1645. X   if (s) s[len] = '^'; ++len;
  1646. X   if (s) s[len] = 64 + (ch & 31) - 32 * (ch == 127); ++len;
  1647. X  }
  1648. X return len;
  1649. X}
  1650. X
  1651. unsigned int fmt_vis(s,t,n) char *s; char *t; unsigned int n;
  1652. X{
  1653. X return fmt_xvis(s,t,n,-1);
  1654. X}
  1655. X
  1656. unsigned int fmt_nvis(s,t,n) char *s; char *t; unsigned int n;
  1657. X{
  1658. X return fmt_xvis(s,t,n,'\n');
  1659. X}
  1660. X
  1661. X/* invertible! */
  1662. unsigned int fmt_rvis(s,t,n) char *s; char *t; unsigned int n;
  1663. X{
  1664. X unsigned int len;
  1665. X for (len = 0;n;--n,++t)
  1666. X  {
  1667. X   int ch;
  1668. X   ch = (int) (unsigned int) (unsigned char) *t;
  1669. X   /* XXX: ASCII dependent! */
  1670. X   if ((ch >= 32) && (ch <= 126) && (ch != '^'))
  1671. X    { if (s) s[len] = ch; ++len; continue; }
  1672. X   if (ch == 127)
  1673. X    { if (s) { s[len] = '^'; s[len + 1] = '?'; } len += 2; continue; }
  1674. X   if (ch == '^')
  1675. X    { if (s) { s[len] = '^'; s[len + 1] = ' '; } len += 2; continue; }
  1676. X   if (ch == 10)
  1677. X    { if (s) { s[len] = '^'; s[len + 1] = '$'; } len += 2; continue; }
  1678. X   if ((ch >= 0) && (ch <= 31))
  1679. X    { if (s) { s[len] = '^'; s[len + 1] = 64 + (ch & 31); } len += 2; continue; }
  1680. X   if (s) { s[len] = '^'; s[len + 1] = 'x'; } len += 2;
  1681. X   if (s) { s[len] = (ch >= 160) ? alow + ((ch/16) - 10) : zero + (ch/16); }
  1682. X   ++len; ch = ch & 15;
  1683. X   if (s) { s[len] = (ch >= 10) ? alow + (ch - 10) : zero + ch; }
  1684. X   ++len;
  1685. X  }
  1686. X s[len] = '\n';
  1687. X return len + 1;
  1688. X}
  1689. X
  1690. unsigned int fmt_unrvis(s,t,n) char *s; char *t; unsigned int n;
  1691. X{
  1692. X unsigned int len;
  1693. X for (len = 0;n;--n,++t)
  1694. X  {
  1695. X   if (*t == '\n')
  1696. X     continue;
  1697. X   if (*t != '^')
  1698. X    {
  1699. X     if (s) *s++ = *t; ++len;
  1700. X     continue;
  1701. X    }
  1702. X   if (n < 2)
  1703. X     return len;
  1704. X   ++t; --n;
  1705. X   if (*t == '?')
  1706. X    { if (s) *s++ = 127; ++len; continue; }
  1707. X   if (*t == ' ')
  1708. X    { if (s) *s++ = '^'; ++len; continue; }
  1709. X   if ((*t >= 64) && (*t <= 95))
  1710. X    { if (s) *s++ = *t - 64; ++len; continue; }
  1711. X   if (*t == '$')
  1712. X    { if (s) *s++ = 10; ++len; continue; }
  1713. X   if (n < 3)
  1714. X     return len;
  1715. X   if (*t != 'x')
  1716. X     return len; /* XXX */
  1717. X   ++t;
  1718. X   if (s)
  1719. X     if (*t < alow)
  1720. X       *s = *t - zero;
  1721. X     else
  1722. X       *s = *t - alow + 10;
  1723. X   if (s)
  1724. X     *s <<= 4;
  1725. X   ++t;
  1726. X   if (s)
  1727. X     if (*t < alow)
  1728. X       *s += *t - zero;
  1729. X     else
  1730. X       *s += *t - alow + 10;
  1731. X   if (s) ++s;
  1732. X   ++len; n -= 2;
  1733. X  }
  1734. X return len;
  1735. X}
  1736. END_OF_FILE
  1737. if test 5780 -ne `wc -c <'fmt.c'`; then
  1738.     echo shar: \"'fmt.c'\" unpacked with wrong size!
  1739. fi
  1740. # end of 'fmt.c'
  1741. fi
  1742. if test -f 'scan.c' -a "${1}" != "-c" ; then 
  1743.   echo shar: Will not clobber existing file \"'scan.c'\"
  1744. else
  1745. echo shar: Extracting \"'scan.c'\" \(5077 characters\)
  1746. sed "s/^X//" >'scan.c' <<'END_OF_FILE'
  1747. X/* scan.c, scan.h: scanning library
  1748. Daniel J. Bernstein, brnstnd@nyu.edu.
  1749. No dependencies.
  1750. No environment requirements.
  1751. X7/18/91: Baseline. scan 1.0, public domain.
  1752. No known patent problems.
  1753. X
  1754. XXXX: still need floating-point scanning
  1755. X
  1756. X*/
  1757. X
  1758. X#include "scan.h"
  1759. X
  1760. X/* just to keep track of what special characters we're using */
  1761. X#define zero '0'
  1762. X#define plus '+'
  1763. X#define minus '-'
  1764. X#define alow 'a'
  1765. X#define acap 'A'
  1766. X#define space ' '
  1767. X#define tab '\t'
  1768. X#define xlow 'x'
  1769. X#define xcap 'x'
  1770. X
  1771. X/* Note that the digits here are defined as '0', '0' + 1, '0' + 2, etc. */
  1772. X/* The letters are defined similarly, starting from 'a' and 'A'. */
  1773. X/* This may produce unintuitive results with a weird character set. */
  1774. X
  1775. unsigned int scan_plusminus(s,sign) char *s; int *sign;
  1776. X{
  1777. X if (*s == plus) { *sign = 1; return 1; }
  1778. X if (*s == minus) { *sign = -1; return 1; }
  1779. X *sign = 1; return 0;
  1780. X}
  1781. X
  1782. unsigned int scan_0x(s,base) char *s; unsigned int *base;
  1783. X{
  1784. X if (*s == zero)
  1785. X  {
  1786. X   if ((s[1] == xlow) || (s[1] == xcap))
  1787. X    { *base = 16; return 2; }
  1788. X   *base = 8; return 1;
  1789. X  }
  1790. X *base = 10; return 0;
  1791. X}
  1792. X
  1793. unsigned int scan_ulong(s,u) char *s; unsigned long *u;
  1794. X{
  1795. X unsigned int pos; unsigned long result; unsigned long c;
  1796. X pos = 0; result = 0;
  1797. X while ((c = (unsigned long) (unsigned char) (s[pos] - zero)) < 10)
  1798. X  { result = result * 10 + c; ++pos; }
  1799. X *u = result; return pos;
  1800. X}
  1801. X
  1802. unsigned int scan_xlong(s,u) char *s; unsigned long *u;
  1803. X{
  1804. X unsigned int pos; unsigned long result; unsigned long c;
  1805. X pos = 0; result = 0;
  1806. X while (((c = (unsigned long) (unsigned char) (s[pos] - zero)) < 10)
  1807. X      ||(((c = (unsigned long) (unsigned char) (s[pos] - alow)) < 6)
  1808. X       &&(c = c + 10))
  1809. X      ||(((c = (unsigned long) (unsigned char) (s[pos] - acap)) < 6)
  1810. X       &&(c = c + 10))
  1811. X       ) /* XXX: this gets the job done */
  1812. X  { result = result * 16 + c; ++pos; }
  1813. X *u = result; return pos;
  1814. X}
  1815. X
  1816. unsigned int scan_nbblong(s,n,base,bext,u)
  1817. char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned long *u;
  1818. X/* Note that n == 0 means scan forever. Hopefully this is a good choice. */
  1819. X{
  1820. X unsigned int pos; unsigned long result; unsigned long c;
  1821. X pos = 0; result = 0;
  1822. X while (((c = (unsigned long) (unsigned char) (s[pos] - zero)) < base)
  1823. X      ||(((c = (unsigned long) (unsigned char) (s[pos] - alow)) < bext)
  1824. X       &&(c = c + base))
  1825. X      ||(((c = (unsigned long) (unsigned char) (s[pos] - acap)) < bext)
  1826. X       &&(c = c + base))
  1827. X       ) /* XXX: this gets the job done */
  1828. X  { result = result * (base + bext) + c; ++pos; if (pos == n) break; }
  1829. X *u = result; return pos;
  1830. X}
  1831. X
  1832. unsigned int scan_uint(s,u) char *s; unsigned int *u;
  1833. X{
  1834. X unsigned int pos; unsigned long result;
  1835. X pos = scan_ulong(s,&result);
  1836. X *u = result; return pos;
  1837. X}
  1838. X
  1839. unsigned int scan_xint(s,u) char *s; unsigned int *u;
  1840. X{
  1841. X unsigned int pos; unsigned long result;
  1842. X pos = scan_xlong(s,&result);
  1843. X *u = result; return pos;
  1844. X}
  1845. X
  1846. unsigned int scan_nbbint(s,n,base,bext,u)
  1847. char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned int *u;
  1848. X{
  1849. X unsigned int pos; unsigned long result;
  1850. X pos = scan_nbblong(s,n,base,bext,&result);
  1851. X *u = result; return pos;
  1852. X}
  1853. X
  1854. unsigned int scan_ushort(s,u) char *s; unsigned short *u;
  1855. X{
  1856. X unsigned int pos; unsigned long result;
  1857. X pos = scan_ulong(s,&result);
  1858. X *u = result; return pos;
  1859. X}
  1860. X
  1861. unsigned int scan_xshort(s,u) char *s; unsigned short *u;
  1862. X{
  1863. X unsigned int pos; unsigned long result;
  1864. X pos = scan_xlong(s,&result);
  1865. X *u = result; return pos;
  1866. X}
  1867. X
  1868. unsigned int scan_nbbshort(s,n,base,bext,u)
  1869. char *s; unsigned int n; unsigned int base; unsigned int bext; unsigned short *u;
  1870. X{
  1871. X unsigned int pos; unsigned long result;
  1872. X pos = scan_nbblong(s,n,base,bext,&result);
  1873. X *u = result; return pos;
  1874. X}
  1875. X
  1876. unsigned int scan_charsetnskip(s,chars,n) char *s; char *chars; unsigned int n;
  1877. X{
  1878. X unsigned int pos;
  1879. X pos = 0;
  1880. X while (chars[s[pos]]) /* user's responsibility to check for null */
  1881. X   if (++pos == n)
  1882. X     break;
  1883. X return pos;
  1884. X}
  1885. X
  1886. unsigned int scan_noncharsetnskip(s,chars,n) char *s; char *chars; unsigned int n;
  1887. X{
  1888. X unsigned int pos;
  1889. X pos = 0;
  1890. X while (!chars[s[pos]]) /* again, user's responsibility to check for null */
  1891. X   if (++pos == n)
  1892. X     break;
  1893. X return pos;
  1894. X}
  1895. X
  1896. unsigned int scan_whitenskip(s,n) char *s; unsigned int n;
  1897. X{
  1898. X unsigned int pos; char c;
  1899. X pos = 0;
  1900. X while (((c = s[pos]) == space) || (c == tab)) /* XXX: this is slow */
  1901. X   if (++pos == n)
  1902. X     break;
  1903. X return pos;
  1904. X}
  1905. X
  1906. unsigned int scan_nonwhitenskip(s,n) char *s; unsigned int n;
  1907. X{
  1908. X unsigned int pos; char c;
  1909. X pos = 0;
  1910. X /* This is the only function without ``str'' in its name where we
  1911. X    check specially for nulls. */
  1912. X while ((c = s[pos]) && (c != space) && (c != tab)) /* XXX: this is slow */
  1913. X   if (++pos == n)
  1914. X     break;
  1915. X return pos;
  1916. X}
  1917. X
  1918. unsigned int scan_strncmp(s,t,n) char *s; char *t; unsigned int n;
  1919. X{
  1920. X unsigned int pos; char c;
  1921. X pos = 0;
  1922. X while ((c = s[pos]) && (c == t[pos]))
  1923. X   if (++pos == n)
  1924. X     break;
  1925. X return pos;
  1926. X}
  1927. X
  1928. unsigned int scan_memcmp(s,t,n) char *s; char *t; unsigned int n;
  1929. X/* This is the only function where n == 0 means do nothing. */
  1930. X{
  1931. X unsigned int pos;
  1932. X pos = 0;
  1933. X while (n) if (s[pos] != t[pos]) break; else { --n; ++pos; }
  1934. X return pos;
  1935. X}
  1936. END_OF_FILE
  1937. if test 5077 -ne `wc -c <'scan.c'`; then
  1938.     echo shar: \"'scan.c'\" unpacked with wrong size!
  1939. fi
  1940. # end of 'scan.c'
  1941. fi
  1942. echo shar: End of archive 4 \(of 9\).
  1943. cp /dev/null ark4isdone
  1944. MISSING=""
  1945. for I in 1 2 3 4 5 6 7 8 9 ; do
  1946.     if test ! -f ark${I}isdone ; then
  1947.     MISSING="${MISSING} ${I}"
  1948.     fi
  1949. done
  1950. if test "${MISSING}" = "" ; then
  1951.     echo You have unpacked all 9 archives.
  1952.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1953. else
  1954.     echo You still need to unpack the following archives:
  1955.     echo "        " ${MISSING}
  1956. fi
  1957. ##  End of shell archive.
  1958. exit 0
  1959.